#include "Triangle.h"
#include "Constants.h"
#include <algorithm>

using namespace std;

Triangle::Triangle(double x1, double y1, double z1,
        double x2, double y2, double z2,
        double x3, double y3, double z3, Shader* s) {
    vertex1.set(x1, y1, z1);
    vertex2.set(x2, y2, z2);
    vertex3.set(x3, y3, z3);
    shader = s;
    bbox = Box(min(x1, min(x2, x3)), max(x1, max(x2, x3)),
            min(y1, min(y2, y3)), max(y1, max(y2, y3)),
            min(z1, min(z2, z3)), max(z1, max(z2, z3)));

    //Check for infinetely small volumes
    if(bbox.maxX - bbox.minX < eps) {
        bbox.minX -= eps;
        bbox.maxX += eps;
    } else if(bbox.maxY - bbox.minY < eps) {
        bbox.minY -= eps;
        bbox.maxY += eps;
    } else if(bbox.maxZ - bbox.minZ < eps) {
        bbox.minZ -= eps;
        bbox.maxZ += eps;
    }

    normal = (vertex2 - vertex1).cross(vertex3 - vertex1);
    normal.normalize();
}

bool Triangle::isHit(Ray& r, double tmin, double tmax, Record& rec) {

    // Cow.txt 2:35

//    cs5721::Vector3D rDirection = r.getDirection();
//
//    cs5721::Vector3D e1 = vertex2 - vertex1;
//    cs5721::Vector3D e2 = vertex3 - vertex1;
//    cs5721::Vector3D h = rDirection.cross(e2);
//    double a = e1.dot(h);
//
//    if(a > -eps && a < eps) return false;
//
//    double f = 1.0 / a;
//    cs5721::Vector3D s = r.getOrigin() - vertex1;
//    double u = f * s.dot(h);
//
//    if(u < 0.0 || u > 1.0) return false;
//
//    cs5721::Vector3D q = s.cross(e1);
//    double v = f * rDirection.dot(q);
//
//    if (v < 0.0 || (u + v) > 1.0) return false;
//
//    double t = f * e2.dot(q);
//    if(t > tmin) {
//        rec.t = t;
//        rec.shader = shader;
//        rec.normal = normal;
//        return true;
//    } else {
//        return false;
//    }

    //Cow.txt 2:32

    cs5721::Vector3D rDirection = r.getDirection();
    cs5721::Vector3D rOrigin = r.getOrigin();
    double v1X = vertex1[0];
    double v1Y = vertex1[1];
    double v1Z = vertex1[2];


    double a = v1X - vertex2[0];
    double b = v1Y - vertex2[1];
    double c = v1Z - vertex2[2];

    double d = v1X - vertex3[0];
    double e = v1Y - vertex3[1];
    double f = v1Z - vertex3[2];

    double g = rDirection[0];
    double h = rDirection[1];
    double i = rDirection[2];

    double j = v1X - rOrigin[0];
    double k = v1Y - rOrigin[1];
    double l = v1Z - rOrigin[2];

    double eiMhf = e * i - h*f;
    double gfMdi = g * f - d*i;
    double dhMeg = d * h - e*g;
    double akMjb = a * k - j*b;
    double jcMal = j * c - a*l;
    double blMkc = b * l - k*c;

    double M = a * eiMhf + b * gfMdi + c * dhMeg;
    double T = -((f * akMjb + e * jcMal + d * blMkc) / M);

    if (T < tmin || T > tmax) {
        return false;
    }

    double Y = (i * akMjb + h * jcMal + g * blMkc) / M;
    if (Y < 0.0 || Y > 1.0) {
        return false;
    }

    double B = (j * eiMhf + k * gfMdi + l * dhMeg) / M;
    if (B < 0.0 || B > (1.0 - Y)) {
        return false;
    }

    rec.t = T;
    rec.shader = shader;
    rec.normal = normal;
    return true;
}

cs5721::Vector3D Triangle::getNormal(cs5721::Vector3D& pointHit) {
    return normal;
}

Box Triangle::boundingBox() {
    return bbox;
}

Shader* Triangle::getShader() {
    return shader;
}

cs5721::Vector3D Triangle::getColor() {
    return shader->getSurfaceColor();
}

void Triangle::print() {
    cout << "Triangle"
            ": ";
    bbox.print();
}